package song;

import org.w3c.dom.Document;
import org.w3c.dom.Node;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.dom.DOMSource;
import java.util.ArrayList;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * Created by IntelliJ IDEA.
 * User: Aleksey
 * Date: 03.01.2008
 * Time: 21:09:19
 */
public class TempoTrack {
    private static Integer ID_SEQ = 179326224;
    private class SignatureEvent {
        Integer id;
        Integer position;
        Integer numerator;
        Integer denominator;

        private SignatureEvent(Integer position, Integer numerator, Integer denominator) {
            this.id = ID_SEQ++;
            this.position = position;
            this.numerator = numerator;
            this.denominator = denominator;
        }

        public Integer getDenominator() {
            return denominator;
        }

        public Integer getId() {
            return id;
        }

        public Integer getNumerator() {
            return numerator;
        }

        public Integer getPosition() {
            return position;
        }
    }

    private class TempoEvent {
        Integer id;
        Double tempoSPB;
        Integer PPQ;

        private TempoEvent(Integer PPQ, Double tempoSPB) {
            this.id = ID_SEQ++;
            this.PPQ = PPQ;
            this.tempoSPB = tempoSPB;
        }

        public Integer getId() {
            return id;
        }

        public Integer getPPQ() {
            return PPQ;
        }

        public Double getTempoSPB() {
            return tempoSPB;
        }
    }

    private ArrayList<SignatureEvent> signatureEvents;
    private ArrayList<TempoEvent> tempoEvents;
    private Integer signatureTrackID;
    private Integer tempoTrackID;

    public TempoTrack() {
        signatureEvents = new ArrayList<SignatureEvent>();
        tempoEvents = new ArrayList<TempoEvent>();
        signatureTrackID = ID_SEQ++;
        tempoTrackID = ID_SEQ++;
    }

    public void addSignatureEvent(Integer position, Integer numerator, Integer denominator) {
        signatureEvents.add(new SignatureEvent(position, numerator, denominator));
    }

    public void addTempoEvent(Integer PPQ, Double tempoSPB) {
        tempoEvents.add(new TempoEvent(PPQ, tempoSPB));
    }

    private void addAttribute(Document doc, Node attr, String name, String value){
        Node val = doc.createAttribute(name);
        val.setTextContent(value);
        attr.getAttributes().setNamedItem(val);
    }

    public void saveToFile(String path, String songName) {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.newDocument();

            Node masterTrack = document.createElement("MasterTrack");

            Node rootObjects = document.createElement("rootObjects");
            Node obj1 = document.createElement("obj");
            addAttribute(document, obj1, "class", "MSignatureTrackEvent");
            addAttribute(document, obj1, "ID", signatureTrackID.toString());
            Node obj2 = document.createElement("obj");
            addAttribute(document, obj2, "class", "MTempoTrackEvent");
            addAttribute(document, obj2, "ID", tempoTrackID.toString());
            masterTrack.appendChild(rootObjects);
            masterTrack.appendChild(obj1);
            masterTrack.appendChild(obj2);

            Node root1 = document.createElement("root");
            addAttribute(document, root1, "name", "Signature Track");
            addAttribute(document, root1, "ID", signatureTrackID.toString());
            Node root2 = document.createElement("root");
            addAttribute(document, root2, "name", "Tempo Track");
            addAttribute(document, root2, "ID", tempoTrackID.toString());
            rootObjects.appendChild(root1);
            rootObjects.appendChild(root2);

            Node list1 = document.createElement("list");
            addAttribute(document, list1, "name", "SignatureEvent");
            addAttribute(document, list1, "type", "obj");
            obj1.appendChild(list1);

            Node list2 = document.createElement("list");
            addAttribute(document, list2, "name", "TempoEvent");
            addAttribute(document, list2, "type", "obj");
            obj2.appendChild(list2);

            Node floatNode = document.createElement("float");
            addAttribute(document, floatNode, "name", "RehearsalTempo");
            addAttribute(document, floatNode, "value", "120");
            obj2.appendChild(floatNode);

            Node intNode = document.createElement("int");
            addAttribute(document, intNode, "name", "RehearsalMode");
            addAttribute(document, intNode, "value", "0");
            obj2.appendChild(intNode);

            for (SignatureEvent signatureEvent : signatureEvents) {
                Node obj = document.createElement("obj");
                addAttribute(document, obj, "class", "MTimeSignatureEvent");
                addAttribute(document, obj, "ID", signatureEvent.getId().toString());

                Node int1 = document.createElement("int");
                addAttribute(document, int1, "name", "Position");
                addAttribute(document, int1, "value", signatureEvent.getPosition().toString());
                Node int2 = document.createElement("int");
                addAttribute(document, int2, "name", "Numerator");
                addAttribute(document, int2, "value", signatureEvent.getNumerator().toString());
                Node int3 = document.createElement("int");
                addAttribute(document, int3, "name", "Denominator");
                addAttribute(document, int3, "value", signatureEvent.getDenominator().toString());
                obj.appendChild(int1);
                obj.appendChild(int2);
                obj.appendChild(int3);
                list1.appendChild(obj);
            }

            for (TempoEvent tempoEvent : tempoEvents) {
                Node obj = document.createElement("obj");
                addAttribute(document, obj, "class", "MTempoEvent");
                addAttribute(document, obj, "ID", tempoEvent.getId().toString());

                Node float1 = document.createElement("float");
                addAttribute(document, float1, "name", "TempoSPB");
                addAttribute(document, float1, "value", tempoEvent.getTempoSPB().toString());
                Node float2 = document.createElement("float");
                addAttribute(document, float2, "name", "PPQ");
                addAttribute(document, float2, "value", tempoEvent.getPPQ().toString());
                obj.appendChild(float1);
                obj.appendChild(float2);
                list2.appendChild(obj);
            }
            TransformerFactory transFactory = TransformerFactory.newInstance();
            Transformer transformer = transFactory.newTransformer();
            DOMSource source = new DOMSource(masterTrack);

            File newXML = new File(path + songName + ".smt");
            FileOutputStream os = new FileOutputStream(newXML);
            StreamResult result = new StreamResult(os);
            transformer.transform(source, result);
            os.close();
        }
        catch (ParserConfigurationException e) {
            e.printStackTrace();
        }
        catch (TransformerConfigurationException e) {
            e.printStackTrace();
        }
        catch (TransformerException e) {
            e.printStackTrace();
        }
        catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}
